package tsif.tcluster.master;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import tsif.tcluster.TClusterMaster;
import tsif.tcluster.service.proxy.TClusterServiceProxy;
import tsif.tcluster.session.TClusterSession;

/**
 * 远程中央控制<br>
 * 
 */
public class TChlusterRemoteMaster extends TClusterMaster {
	/** 服务缓存存活时间(唯一服务key) **/
	protected static final long SERVICE_CACHE_LIVE_TIME = 30 * 1000;

	protected String host;
	protected int port;

	protected ConcurrentMap<String, UniqueServiceCache> uniqueServiceNames;

	@Override
	public boolean init(TClusterServiceProxy serviceProxy, long serviceId, Object... args) throws Exception {
		if (!super.init(serviceProxy, serviceId, args)) {
			return false;
		}
		this.host = (String) args[0];
		this.port = (int) args[1];
		uniqueServiceNames = new ConcurrentHashMap<>();
		return true;
	}

	/** 尝试执行连接 **/
	public boolean tryToConnect(int waitTime, int sleepTime) {
		// wait to connect
		long startTime = System.currentTimeMillis();
		Exception lastException = null; // 最后的错误
		while (true) {
			try {
				// check to connect
				TClusterSession session = super.getSession(MASTER_CONTEXT_ID);
				if (session != null) {
					return true;
				}
			} catch (Exception e) {
				lastException = e;
				// System.err.println("connect to master error! host=" + host + " port=" + port);
				// e.printStackTrace();
			}

			// check wait time
			long nowTime = System.currentTimeMillis();
			long dt = nowTime - startTime;
			if (dt >= waitTime) {
				break;
			}
			try {
				Thread.sleep(sleepTime);
			} catch (Exception e) {
			}
		}

		// show exception
		if (lastException != null) {
			lastException.printStackTrace();
		}
		return false;
	}

	@Override
	public void destroy() {
	}

	@Override
	public void update() {

	}

	public String getHost() {
		return host;
	}

	public int getPort() {
		return port;
	}

	@Override
	public void abort(int shutdownTime, boolean shutdownSelf) {
		this.accessor.send(MASTER_SERVICE_ID, "abort", shutdownTime, shutdownSelf); // 不等待, 等待会出现callback超时.
	}

	@Override
	public int registerContext(int contextId, String host, int port) {
		return accessor.call(MASTER_SERVICE_ID, "registerContext", contextId, host, port);
	}

	@Override
	protected void updateContext(int contextId) {
		accessor.send(MASTER_SERVICE_ID, "updateContext", contextId);
	}

	@Override
	public boolean removeContext(int contextId) {
		return accessor.call(MASTER_SERVICE_ID, "removeContext", contextId);
	}

	@Override
	public long bindUniqueService(String key, long serviceId, boolean replace) {
		return accessor.call(MASTER_SERVICE_ID, "bindUniqueService", key, serviceId, replace);
	}

	@Override
	public void unbindUniqueService(String key, long serviceId) {
		accessor.send(MASTER_SERVICE_ID, "unbindUniqueService", key, serviceId);
	}

	@Override
	public long getUniqueService(String key) {
		// get cache
		UniqueServiceCache cache = uniqueServiceNames.get(key);
		Long uniqueServiceId = null;
		if (cache != null) {
			long dt = System.currentTimeMillis() - cache.updateTime;
			if (dt < SERVICE_CACHE_LIVE_TIME) {
				uniqueServiceId = cache.serviceId; // 有效
			}
		}

		// check cache value
		if (uniqueServiceId != null) {
			return uniqueServiceId;
		}
		// get
		uniqueServiceId = accessor.call(MASTER_SERVICE_ID, "getUniqueService", key);
		if (uniqueServiceId == null) {
			return 0L;
		}

		// create new cache
		if (cache == null) {
			cache = new UniqueServiceCache();
			UniqueServiceCache old = uniqueServiceNames.putIfAbsent(key, cache);
			cache = (old != null) ? old : cache;
		}
		cache.serviceId = uniqueServiceId;
		cache.updateTime = System.currentTimeMillis();
		return uniqueServiceId;
	}

	@Override
	public ContextNode getContext(int contextId) {
		return accessor.call(MASTER_SERVICE_ID, "getContext", contextId);
	}

	@Override
	public void dispose(long serviceId, String funcName, Object[] args, IReturn<Object> ret) {
		throw new RuntimeException("can't dispose by remote master!");
	}

	/** 唯一节点缓存 **/
	protected class UniqueServiceCache {
		public Long serviceId;
		public long updateTime;
	}
}
